#include <fstream>

#include "properties_reader.h"

CPropertiesReader::CPropertiesReader(const std::string& pFilename, 
		EOpenFlags pFlags) :
	mFilename(pFilename),
	mLoaded(false),
	mFlags(pFlags)
{

}

CPropertiesReader::~CPropertiesReader()
{
	dropConfig();
}

bool CPropertiesReader::createSection(const std::string& pSection)
{
	if ((!mLoaded) && (mFlags == OPEN_AUTOLOAD))
	{
		loadIniFile();
	}

	if (!containsSection(pSection))
	{
		mSections.insert(std::make_pair(pSection, new TSection));
	}

	return true;
}

bool CPropertiesReader::containsSection(const std::string& pSection)
{
	if ((!mLoaded) && (mFlags == OPEN_AUTOLOAD))
	{
		loadIniFile();
	}

	TSection* lMap;
	if (!getMapForSection(pSection, lMap)) {
		return false;
	}

	return true;
}

bool CPropertiesReader::containsKey(const std::string& pSection,
		const std::string& pKey)
{
	if ((!mLoaded) && (mFlags == OPEN_AUTOLOAD))
	{
		loadIniFile();
	}

	std::string lValue;
	if (!getValueForKey(pSection, pKey, lValue)) {
		return false;
	}

	return true;
}

bool CPropertiesReader::getValue(const std::string& pSection,
		const std::string& pKey, std::string& pValue)
{
	if ((!mLoaded) && (mFlags == OPEN_AUTOLOAD))
	{
		loadIniFile();
	}

	if (!getValueForKey(pSection, pKey, pValue)) {
		return false;
	}

	return true;
}

bool CPropertiesReader::setValue(const std::string& pSection,
		const std::string& pKey, const std::string& pValue)
{
	if ((!mLoaded) && (mFlags == OPEN_AUTOLOAD))
	{
		loadIniFile();
	}

	// only existing keys can get new values (for now)
	if (!containsKey(pSection, pKey)) {
		return false;
	}

	TSection* lMap;
	getMapForSection(pSection, lMap);
	(*lMap)[pKey] = pValue;

	return true;
}

bool CPropertiesReader::createKey(const std::string& pSection,
		const std::string& pKey, const std::string& pValue)
{
	if (containsKey(pSection, pKey))
	{
		return false;
	}

	mSections[pSection]->insert(std::make_pair(pKey, pValue));

	return true;
}


void CPropertiesReader::changeFile(const std::string& pFilename)
{
	if (pFilename != "") {
		mFilename = pFilename;
	}

	dropConfig();
}

void CPropertiesReader::dump(std::ostream& pOut)
{
	if ((!mLoaded) && (mFlags == OPEN_AUTOLOAD))
	{
		loadIniFile();
	}

	TSectionIterator lIt;
	TSection* lMap;
	TKeyIterator lItS;

	for (lIt = mSections.begin(); lIt != mSections.end(); lIt++) {
		// section heading (UNIX line-endings)
		pOut << "[" << lIt->first << "]";
		pOut.put(0x0A);

		lMap = lIt->second;
		for (lItS = lMap->begin(); lItS != lMap->end(); lItS++) {
			// keys and values (UNIX line-endings)
			pOut << lItS->first << "=" << lItS->second;
			pOut.put(0x0A);
		}

		// UNIX line-ending
		pOut.put(0x0A);
	}
}

bool CPropertiesReader::write()
{
	if (mFilename == "")
	{
		return false;
	}

	std::ofstream lConfigStream;
	lConfigStream.open(mFilename.c_str(), std::ios::out|std::ios::binary);
	dump(lConfigStream);

	if (lConfigStream.bad()) { // not eof, another bad bit is set
		lConfigStream.close();
		return false;
	}

	lConfigStream.close();
	return true;
}

bool CPropertiesReader::loadIniFile()
{
	// the ini file contains of empty lines, lines starting with '#' (comments),
	// lines starting with '[' and ending with ']' (section headings) and lines
	// with key=value[ value[ value...]] pairs

	if (mLoaded)
	{
		return true;
	}
	int lPos(0);
	std::string lLine, lSection, lKey, lValue;
	std::ifstream lReader;
	TSection* lMap = NULL;

	lReader.open(mFilename.c_str(), std::ios::in);
	if (lReader.fail()) {
		return false;
	}

	// read ini file, line by line until eof
	while (lReader.good()) {
		getline(lReader, lLine);

		// empty line or comment
		if (lLine == "" || lLine[0] == '#') {
			continue;
		}

		// section heading
		if (lLine[0] == '[') {
			lPos = lLine.find(']');
			lSection = lLine.substr(1, lPos - 1);

			// each section gets an own map
			lMap = new TSection;
			mSections.insert(std::make_pair(lSection, lMap));
			continue;
		}

		// key=value
		if (lMap == NULL) { // no section was read until now
			lReader.close();
			return false;
		}

		lPos = lLine.find('=');
		if (lPos == -1) { // something's wrong
			lReader.close();
			return false;
		}

		lKey = lLine.substr(0, lPos);
		lValue = lLine.substr(lPos + 1);

		lMap->insert(std::make_pair(lKey, lValue));
	}

	if (lReader.bad()) { // not eof, another bad bit is set
		lReader.close();
		return false;
	}

	lReader.close();
	mLoaded = true;
	return true;
}

bool CPropertiesReader::getSectionIteratorBegin(TSectionIterator& pIterator)
{
	if ((!mLoaded) && (mFlags == OPEN_AUTOLOAD))
	{
		loadIniFile();
	}
	pIterator = mSections.begin();
	return true;
}

bool CPropertiesReader::getSectionIteratorEnd(TSectionIterator& pIterator)
{
	if ((!mLoaded) && (mFlags == OPEN_AUTOLOAD))
	{
		loadIniFile();
	}
	pIterator = mSections.end();
	return true;
}

bool CPropertiesReader::getKeyIteratorBegin(std::string pSection, TKeyIterator& pIterator)
{
	if ((!mLoaded) && (mFlags == OPEN_AUTOLOAD))
	{
		loadIniFile();
	}
	TSectionIterator lIt;
	lIt = mSections.find(pSection);
	if (lIt == mSections.end())
	{
		return false;
	}

	pIterator = (*(lIt->second)).begin();
	return true;
}

bool CPropertiesReader::getKeyIteratorEnd(std::string pSection, TKeyIterator& pIterator)
{
	if ((!mLoaded) && (mFlags == OPEN_AUTOLOAD))
	{
		loadIniFile();
	}
	TSectionIterator lIt;
	lIt = mSections.find(pSection);
	if (lIt == mSections.end())
	{
		return false;
	}

	pIterator = (*(lIt->second)).end();
	return true;
}

void CPropertiesReader::dropConfig()
{
	if (mSections.empty() || !mLoaded) { // nothing to do
		mLoaded = false;
		return;
	}

	TSectionIterator lIt;

	for (lIt = mSections.begin(); lIt != mSections.end(); lIt++) {
		lIt->second->clear();
		delete lIt->second;
	}

	mSections.clear();
	mLoaded = false;
}

bool CPropertiesReader::getMapForSection(const std::string& pSection,
		TSection*& pMap)
{
	TSectionIterator lIt;

	// search section name in keys of mSections map ...
	lIt = mSections.find(pSection);
	if (lIt == mSections.end()) {
		return false;
	}

	// ... and save the value (pointer to key, value map)
	pMap = lIt->second;
	return true;
}

bool CPropertiesReader::getValueForKey(const std::string& pSection,
		const std::string& pKey, std::string& pValue)
{
	TSection* lMap;

	if (!getMapForSection(pSection, lMap)) {
		return false;
	}

	TKeyIterator lIt;

	// search key from section key, value map ...
	lIt = lMap->find(pKey);
	if (lIt == lMap->end()) {
		return false;
	}

	// ... and return its value
	pValue = lIt->second;
	return true;
}

